
/**
 * @author LC
 *
 */
package com.cyys.modules.core.exception;


import com.alibaba.fastjson.JSON;
import com.cyys.common.ql.AjaxJson;
import com.cyys.common.utils.http.HttpKit;
import org.apache.shiro.authc.AuthenticationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@ControllerAdvice
//@ResponseBody
public class GlobalExceptionHandler {
	private static Logger log = LoggerFactory.getLogger(ExceptionHandler.class);
	private static final String logExceptionFormat = "Capture Exception By GlobalExceptionHandler: Code: %s Detail: %s";

	public static final String ERROR_VIEW = "error";

	//运行时异常
	@ExceptionHandler(RuntimeException.class)
	public Object runtimeExceptionHandler(RuntimeException ex) {
		return resultFormat(1, ex);
	}

	//空指针异常
	@ExceptionHandler(NullPointerException.class)
	public Object nullPointerExceptionHandler(NullPointerException ex) {
		return resultFormat(2, ex);
	}

	//类型转换异常
	@ExceptionHandler(ClassCastException.class)
	public Object classCastExceptionHandler(ClassCastException ex) {
		return resultFormat(3, ex);
	}

	//IO异常
	@ExceptionHandler(IOException.class)
	public Object iOExceptionHandler(IOException ex) {
		return resultFormat(4, ex);
	}

	//未知方法异常
	@ExceptionHandler(NoSuchMethodException.class)
	public Object noSuchMethodExceptionHandler(NoSuchMethodException ex) {
		return resultFormat(5, ex);
	}

	//数组越界异常
	@ExceptionHandler(IndexOutOfBoundsException.class)
	public Object indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) {
		return resultFormat(6, ex);
	}

	//400错误
	@ExceptionHandler({HttpMessageNotReadableException.class})
	public Object requestNotReadable(HttpMessageNotReadableException ex) {
		return resultFormat(7, ex);
	}

	//400错误
	@ExceptionHandler({TypeMismatchException.class})
	public Object requestTypeMismatch(TypeMismatchException ex) {
		return resultFormat(8, ex);
	}

	//400错误
	@ExceptionHandler({MissingServletRequestParameterException.class})
	public Object requestMissingServletRequest(MissingServletRequestParameterException ex) {
		return resultFormat(9, ex);
	}

	//405错误
	@ExceptionHandler({HttpRequestMethodNotSupportedException.class})
	public Object request405(HttpRequestMethodNotSupportedException ex) {
		return resultFormat(10, ex);
	}

	//406错误
	@ExceptionHandler({HttpMediaTypeNotAcceptableException.class})
	public Object request406(HttpMediaTypeNotAcceptableException ex) {
		return resultFormat(11, ex);
	}

	//500错误
	@ExceptionHandler({ConversionNotSupportedException.class, HttpMessageNotWritableException.class})
	public Object server500(RuntimeException ex) {
		return resultFormat(12, ex);
	}

	//栈溢出
	@ExceptionHandler({StackOverflowError.class})
	public Object requestStackOverflow(StackOverflowError ex) {
		return resultFormat(13, ex);
	}

	//除数不能为0
	@ExceptionHandler({ArithmeticException.class})
	public Object arithmeticException(ArithmeticException ex) {
		return resultFormat(14, ex);
	}


	//图片上传大小超出限制
	@ExceptionHandler({MaxUploadSizeExceededException.class})
	public Object maxUploadSizeExceededException(ArithmeticException ex) {
		return resultFormat(15, ex);
	}

	//处理 form data方式调用接口校验失败抛出的异常
	@ExceptionHandler({BindException.class})
	public Object bindExceptionException(BindException ex) {
		return resultFormat(20, ex);
	}

	//处理单个参数校验失败抛出的异常
	@ExceptionHandler(ConstraintViolationException.class)
	public Object constraintViolationExceptionHandler(ConstraintViolationException ex) {
		return resultFormat(21, ex);
	}

	//处理 json 请求体调用接口校验失败抛出的异常
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public Object methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
		return resultFormat(22, ex);
	}


	//处理 json 请求体调用接口校验失败抛出的异常
	@ExceptionHandler(AuthenticationException.class)
	public Object authenticationExceptionHandler(AuthenticationException ex) {
		return resultFormat(23, ex);
	}

	//其他错误
	@ExceptionHandler({Exception.class})
	public Object exception(Exception ex) {
		return resultFormat(16, ex);
	}

	//其他错误
	@ExceptionHandler({NoHandlerFoundException.class})
	public Object noHandlerFoundException(Exception ex) {
		return resultFormat(17, ex);
	}

	private <T extends Throwable> Object resultFormat(Integer code, T ex) {
		HttpServletRequest request = HttpKit.getRequest();
		HttpServletResponse response = HttpKit.getResponse();
		log.error("【抛出异常】--异常路径为：" +  HttpKit.getRequest().getServletPath() + "\n【异常信息】--" +  ex);
		log.error(ex.getStackTrace().toString());
		ex.printStackTrace();
		if (isAjax(request)) {
			response.setCharacterEncoding("UTF-8");
			response.setContentType("application/json; charset=utf-8");
			PrintWriter writer = null;
			try {
				writer = response.getWriter();
			} catch (IOException e) {
				e.printStackTrace();
			}
			AjaxJson aj = new AjaxJson();
			aj.setErrorCode(code.toString());
			if(code == 20 || code == 22){
				BindException e = (BindException) ex;
				List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
				List<String> collect = fieldErrors.stream()
						.map(o -> o.getDefaultMessage())
						.collect(Collectors.toList());
				aj.setMsg(collect.toString());
			}else if(code == 21){
				ConstraintViolationException e = (ConstraintViolationException) ex;
				Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
				List<String> collect = constraintViolations.stream()
						.map(o -> o.getMessage())
						.collect(Collectors.toList());
				aj.setMsg(collect.toString());
			}else{
				aj.setMsg(ex.getClass().getSimpleName() + "：" + ex.getMessage());
			}
			aj.setSuccess(false);
			String sOut = "";
			StackTraceElement[] trace = ex.getStackTrace();
			for (StackTraceElement s : trace) {
				sOut += "\t at " + s + "<br>";
			}
			aj.put("excetion", sOut);
			//具体操作
			writer.write(JSON.toJSONString(aj));
			writer.flush();
			writer.close();
			return null;
		}else{
			ModelAndView mav = new ModelAndView();
			mav.addObject("url", request.getRequestURL());
			mav.addObject("exception", ex.getMessage());
			String sOut = "";
			StackTraceElement[] trace = ex.getStackTrace();
			for (StackTraceElement s : trace) {
				sOut += "\t at " + s + "<br>";
			}
			mav.addObject("stackTrace", sOut);
			mav.setViewName(ERROR_VIEW);
			return mav;
		}
	}

	/**
	 * 判断是否是ajax请求
	 */
	public static boolean isAjax(HttpServletRequest httpRequest) {
		return (httpRequest.getHeader("X-Requested-With") != null
				&& "XMLHttpRequest"
				.equals(httpRequest.getHeader("X-Requested-With").toString()));
	}

}